package com.ohos.carbon.slice.refresh;

import com.ohos.carbon.utils.ScrollUtil;
import com.ohos.carbon.slice.gesture.GestureDetector;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ScrollHelper;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 下拉刷新组件
 *
 */
public class SwipeRefreshLayout extends ComponentContainer implements
        Component.TouchEventListener, Component.LayoutRefreshedListener, IRefresh {

    private GestureDetector mGestureDetector;
    private HeadOverComponent.RefreshState mState;
    private RefreshListener mRefreshListener;
    /**
     * 下拉刷新的头部组件
     */
    protected HeadOverComponent mHeadOverComponent;
    private int mLastY;
    /**
     * 下拉刷新的时候是否禁止滚动
     */
    private boolean disableRefreshScroll;
    private AutoScroller mAutoScroll;

    public SwipeRefreshLayout(Context context) {
        this(context, null);
    }

    public SwipeRefreshLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public SwipeRefreshLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        // 调用setTouchEventListener，让onTouchEvent方法执行
        setTouchEventListener(this);
        // 调用setLayoutRefreshedListener，让onRefresh方法执行
        setLayoutRefreshedListener(this);
        mGestureDetector = new GestureDetector(gestureDetector);
        mAutoScroll = new AutoScroller(this);
    }

    private final GestureDetector.OnGestureListener gestureDetector = new GestureDetector.OnGestureListener() {
        @Override
        public boolean onScroll(TouchEvent e1, TouchEvent e2, float distanceX, float distanceY) {
            if (Math.abs(distanceX) > Math.abs(distanceY)) {
                // 发生横向滚动，不单独处理事件，让系统正常分发事件
                return false;
            }
            if (mRefreshListener != null && !mRefreshListener.enableRefresh()) {
                // 不允许下拉刷新，不单独处理事件，让系统正常分发事件
                return false;
            }
            if (disableRefreshScroll && mState == HeadOverComponent.RefreshState.STATE_REFRESH) {
                // 刷新时禁止列表页滚动，返回true，消费事件，不让列表页滚动
                return true;
            }
            Component head = getComponentAt(0);
            // 查找可以滚动的组件
            Component child = ScrollUtil.findScrollableChild(SwipeRefreshLayout.this);
            // 判断组件是否正在滚动
            if (ScrollUtil.childScrolled(child)) {
                // 组件发生了滚动，不单独处理事件，让系统正常分发事件
                return false;
            }
            // 没有刷新或者没有达到可刷新的距离，并且头部已经划出或者下拉
            if ((mState != HeadOverComponent.RefreshState.STATE_REFRESH || head.getBottom() <= mHeadOverComponent.mPullRefreshHeight) &&
                    (head.getBottom() != 0 || distanceY <= 0)) {
                // 还在滑动中
                if (mState != HeadOverComponent.RefreshState.STATE_OVER_RELEASE) {
                    int offsetY;
                    // 根据阻尼计算需要滑动的距离
                    if (head.getTop() < mHeadOverComponent.mPullRefreshHeight) {
                        // 没到可刷新的距离，减少阻尼
                        offsetY = (int) (mLastY / mHeadOverComponent.minDamp);
                    } else {
                        // 达到可刷新的距离，加大阻尼
                        offsetY = (int) (mLastY / mHeadOverComponent.maxDamp);
                    }
                    // 如果是正在刷新状态，则不允许在滑动的时候改变状态
                    // 用户手动下拉触发滑动，moveDown方法的第二个参数传false
                    boolean bool = moveDown(offsetY, false);
                    mLastY = (int) - distanceY;
                    return bool;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
    };

    /**
     * 根据偏移量滚动头部组件和子组件
     *
     * @param offsetY 偏移量
     * @param auto false手动触发滚动，true自动触发滚动
     * @return
     */
    private boolean moveDown(int offsetY, boolean auto) {
        Component head = getComponentAt(0);
        Component child = getComponentAt(1);
        int childTop = child.getTop() + offsetY;
        if (childTop <= 0) {
            // 异常情况处理
            offsetY = -child.getTop();
            // 移动到原始位置
            offsetTopAndBottom(head, offsetY);
            offsetTopAndBottom(child, offsetY);
            if (mState != HeadOverComponent.RefreshState.STATE_REFRESH) {
                mState = HeadOverComponent.RefreshState.STATE_INIT;
            }
        } else if (mState == HeadOverComponent.RefreshState.STATE_REFRESH && childTop > mHeadOverComponent.mPullRefreshHeight) {
            // 正在刷新中，禁止继续下拉
            return false;
        } else if (childTop <= mHeadOverComponent.mPullRefreshHeight) {
            // 没有超出可刷新的距离
            if (mHeadOverComponent.getState() != HeadOverComponent.RefreshState.STATE_REFRESH && !auto) {
                // 头部开始显示
                mHeadOverComponent.onVisible();
                mHeadOverComponent.setState(HeadOverComponent.RefreshState.STATE_VISIBLE);
                mState = HeadOverComponent.RefreshState.STATE_VISIBLE;
            }
            offsetTopAndBottom(head, offsetY);
            offsetTopAndBottom(child, offsetY);
            if (childTop == mHeadOverComponent.mPullRefreshHeight && mState == HeadOverComponent.RefreshState.STATE_OVER_RELEASE) {
                // 开始刷新
                refresh();
            }
        } else {
            if (mHeadOverComponent.getState() != HeadOverComponent.RefreshState.STATE_OVER && !auto) {
                // 超出刷新位置
                mHeadOverComponent.onOver();
                mHeadOverComponent.setState(HeadOverComponent.RefreshState.STATE_OVER);
            }
            offsetTopAndBottom(head, offsetY);
            offsetTopAndBottom(child, offsetY);
        }
        if (mHeadOverComponent != null) {
            mHeadOverComponent.onScroll(head.getBottom(), mHeadOverComponent.mPullRefreshHeight);
        }
        return true;
    }

    /**
     * 修改组件的位置来达到滚动的效果，只修改组件的垂直位置
     *
     * @param component
     * @param offset
     */
    public void offsetTopAndBottom(Component component, int offset) {
        component.setTop(component.getTop() + offset);
        component.setBottom(component.getBottom() + offset);
        component.setComponentPosition(component.getLeft(), component.getTop(), component.getRight(), component.getBottom());
    }

    /**
     * 修改组件的位置来达到滚动的效果，只修改组件的水平位置
     *
     * @param component
     * @param offset
     */
    public void offsetLeftAndRight(Component component, int offset) {
        component.setLeft(component.getLeft() + offset);
        component.setRight(component.getRight() + offset);
        component.setComponentPosition(component.getLeft(), component.getTop(), component.getRight(), component.getBottom());
    }

    /**
     * 开始刷新
     */
    private void refresh() {
        if (mRefreshListener != null) {
            mHeadOverComponent.onRefresh();
            mRefreshListener.onRefresh();
            mState = HeadOverComponent.RefreshState.STATE_REFRESH;
            mHeadOverComponent.setState(HeadOverComponent.RefreshState.STATE_REFRESH);
        }
    }

    /**
     * 当组件重新布局的时候回调，在该方法里面可以调用setComponentPosition方法来确定组件的位置。
     * 要想该方法执行，需要调用setLayoutRefreshedListener方法，这里在构造方法里面调用了setLayoutRefreshedListener方法
     *
     * @param component 当前组件对象，也就是SwipeRefreshLayout
     */
    @Override
    public void onRefreshed(Component component) {
        int left = component.getLeft();
        int top = component.getTop();
        int right = component.getRight();
        int bottom = component.getBottom();
        Component head = getComponentAt(0);
        Component child = getComponentAt(1);
        if (head != null && child != null) {
            int childTop = child.getTop();
            if (mState == HeadOverComponent.RefreshState.STATE_REFRESH) {
                head.setComponentPosition(0, mHeadOverComponent.mPullRefreshHeight - head.getHeight(), right, mHeadOverComponent.mPullRefreshHeight);
                child.setComponentPosition(0, mHeadOverComponent.mPullRefreshHeight, right, mHeadOverComponent.mPullRefreshHeight + child.getHeight());
            } else {
                head.setComponentPosition(0, childTop - head.getHeight(), right, childTop);
                child.setComponentPosition(0, childTop, right, childTop + child.getHeight());
            }

            Component other;
            // 剩下的组件能够不跟随手势移动以实现一些特殊效果，如悬浮的效果
            for (int i = 2; i < getChildCount(); ++i) {
                other = getComponentAt(i);
                other.setComponentPosition(0, top, right, bottom);
            }
        }
    }

    /**
     * 处理分派给组件的触摸事件，要想该方法执行，需要调用setTouchEventListener方法，这里在构造方法里面调用了setTouchEventListener方法
     *
     * @param component
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent ev) {
        Component head = getComponentAt(0);
        if (ev.getAction() == TouchEvent.CANCEL ||
                ev.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            // 松开手
            if (head.getBottom() > 0) {
                // 头部视图被拉下来了
                if (mState != HeadOverComponent.RefreshState.STATE_REFRESH) {
                    // 松开手的时候不是刷新状态
                    recover(head.getBottom());
                    return false;
                }
            }
            mLastY = 0;
        }
        // 处理完抬起事件后，将手势交给手势处理器
        boolean consumed = mGestureDetector.onTouchEvent(ev);
        if (head.getBottom() != 0 && consumed) {
            // 头部视图被拉下来了，并且消费了事件
            return true;
        }
        if (head.getBottom() != 0
                && mState != HeadOverComponent.RefreshState.STATE_INIT
                && mState != HeadOverComponent.RefreshState.STATE_REFRESH) {
            // 头部视图被拉下来了，并且当前状态不是初始状态，并且不是刷新状态
            return true;
        }
        if (consumed) {
            // 消费事件，事件不再往下传递
            return true;
        } else {
            return false;
        }
    }

    private void recover(int dis) {
        if (mRefreshListener != null && dis > mHeadOverComponent.mPullRefreshHeight) {
            /*
             * 由于下拉的距离超过了下拉刷新的最小距离，所以需先要向上滚动一段距离，
             * 也就是需要滚动到mHiOverView.mPullRefreshHeight这个距离，然后才开始刷新，
             * 需要向上滚动多少距离才能到达mHiOverView.mPullRefreshHeight这个距离呢？
             * dis - mHiOverView.mPullRefreshHeight就是需要向上滚动的距离
             */
            mAutoScroll.recover(dis - mHeadOverComponent.mPullRefreshHeight);
            mState = HeadOverComponent.RefreshState.STATE_OVER_RELEASE;
        } else {
            // 没有达到下拉刷新的最小距离，也就是没有达到mHiOverView.mPullRefreshHeight这个距离，
            // 此时就应当直接向上滚动，也就是隐藏头部视图
            mAutoScroll.recover(dis);
        }
    }

    /**
     * 下拉刷新的时候是否禁止滚动
     *
     * @param disableRefreshScroll true禁止滚动，false允许滚动
     */
    @Override
    public void setDisableRefreshScroll(boolean disableRefreshScroll) {
        this.disableRefreshScroll = disableRefreshScroll;
    }

    /**
     * 刷新完成
     */
    @Override
    public void refreshFinish() {
        Component head = getComponentAt(0);
        mHeadOverComponent.onFinish();
        mHeadOverComponent.setState(HeadOverComponent.RefreshState.STATE_INIT);
        int bottom = head.getBottom();
        if (bottom > 0) {
            // 刷新完成隐藏头部视图
            recover(bottom);
        }
        mState = HeadOverComponent.RefreshState.STATE_INIT;
    }

    /**
     * 设置下拉刷新监听
     *
     * @param listener 监听
     */
    @Override
    public void setRefreshListener(RefreshListener listener) {
        mRefreshListener = listener;
    }

    /**
     * 设置下拉刷新的头部组件
     *
     * @param headOverComponent 下拉刷新的头部组件
     */
    @Override
    public void setRefreshOverComponent(HeadOverComponent headOverComponent) {
        if (mHeadOverComponent != null) {
            removeComponent(mHeadOverComponent);
        }
        mHeadOverComponent = headOverComponent;
        LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
        addComponent(mHeadOverComponent, 0, config);
    }

    private static class AutoScroller implements Runnable {

        private final ScrollHelper mScroller;
        private int mLastY;
        private boolean mIsFinish;

        private EventHandler mEventHandler;
        private SwipeRefreshLayout mSwipeRefreshLayout;

        public AutoScroller(SwipeRefreshLayout swipeRefreshLayout) {
            mSwipeRefreshLayout = swipeRefreshLayout;
            mScroller = new ScrollHelper();
            mEventHandler = new EventHandler(EventRunner.getMainEventRunner());
            mIsFinish = true;
        }

        @Override
        public void run() {
            if (!mScroller.isFinished()) {
                mScroller.updateScroll();
                mSwipeRefreshLayout.moveDown(mLastY - mScroller.getCurrValue(ScrollHelper.AXIS_Y), true);
                mLastY = mScroller.getCurrValue(ScrollHelper.AXIS_Y);
                mEventHandler.postTask(this);
            } else {
                mEventHandler.removeTask(this);
                mIsFinish = true;
            }
        }

        void recover(int dis) {
            mEventHandler.removeTask(this);
            mLastY = 0;
            mIsFinish = false;
            mScroller.startScroll(0, 0, 0, dis);
            mEventHandler.postTask(this);
        }

        boolean isFinish() {
            return mIsFinish;
        }
    }
}
